/**
 * 
 */
package idv.takeshi.software.productline.bookstore.presentation.web.controller;

import idv.takeshi.software.productline.bookstore.application.service.BrowseBooksService;
import idv.takeshi.software.productline.bookstore.application.service.PlaceOrderService;
import idv.takeshi.software.productline.bookstore.application.service.UserRegistrationSerivce;
import idv.takeshi.software.productline.bookstore.domain.model.category.Category;
import idv.takeshi.software.productline.bookstore.domain.model.deliveryservice.DeliveryService;
import idv.takeshi.software.productline.bookstore.domain.model.order.CashInConvenientStore;
import idv.takeshi.software.productline.bookstore.domain.model.order.DuplicateInvoice;
import idv.takeshi.software.productline.bookstore.domain.model.order.DuplicateInvoiceWithCashier;
import idv.takeshi.software.productline.bookstore.domain.model.order.Invoice;
import idv.takeshi.software.productline.bookstore.domain.model.order.Order;
import idv.takeshi.software.productline.bookstore.domain.model.order.OrderDuplicateException;
import idv.takeshi.software.productline.bookstore.domain.model.order.OrderFactory;
import idv.takeshi.software.productline.bookstore.domain.model.shoppingcart.ShoppingCart;
import idv.takeshi.software.productline.bookstore.domain.model.useraccount.UserAccount;
import idv.takeshi.software.productline.bookstore.presentation.dto.OrderDto;

import java.security.Principal;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.context.request.WebRequest;

/**
 * A Controller for order operations.
 * @author takeshi
 *
 */
@Controller("placeOrderController")
@RequestMapping("/order")
@SessionAttributes({"order", "userAccount"})
public class PlaceOrderController {
	
	private static final Log log = LogFactory.getLog(PlaceOrderController.class);
	
	private static final String ORDER_DTO_NAME = "orderDto";

	@Autowired
	private UserRegistrationSerivce individualAccountRegistrationService;
	
	@Autowired
	private BrowseBooksService browseBooksService;
	
	@Autowired
	private PlaceOrderService placeOrderService;
	
	@Autowired
	private OrderFactory orderFactory;
	
	@Autowired
	private MessageSource messageSource;
	
	private Integer convenientStoreDeliveryServiceId;
	
	@RequestMapping(value = "/orderDetailsInfo.html", method = RequestMethod.GET)
	public void createOrderDto(ModelMap modelMap, Principal principal){
		Validate.notNull(principal, "principal shall always not be null");
		Validate.notEmpty(principal.getName(), "principal.getName() shall always not be null/empty");
		if(!modelMap.containsAttribute(ORDER_DTO_NAME)){
			UserAccount userAccount = this.individualAccountRegistrationService.
				getUserAccountByName(principal.getName());
			
			OrderDto orderDto = new OrderDto(userAccount);
			modelMap.put(ORDER_DTO_NAME, orderDto);
			modelMap.put("userAccount", userAccount);
		} 
	}
	
	@RequestMapping(value = "/confirmOrder.html", method = RequestMethod.POST)
	public String createOrder(
			@ModelAttribute(ORDER_DTO_NAME) @Valid final OrderDto orderDto,
			final BindingResult result, 
			ModelMap modelMap, WebRequest request, Locale locale){
		
		if(result.hasErrors()){
			return "/order/orderDetailsInfo";
		}
		validateOrderDto(orderDto, result, locale);
		if(result.hasErrors()){
			return "/order/orderDetailsInfo";
		}
		
		DeliveryService deliveryService = this.placeOrderService.getDeliveryServiceById(
				orderDto.getDeliveryServiceType());
		Order order = orderDto.createModel(orderFactory, deliveryService, 
				convenientStoreDeliveryServiceId, 
				(ShoppingCart) request.getAttribute("shoppingCart", WebRequest.SCOPE_SESSION));
		modelMap.put("order", order);
		return "/order/confirmOrder";
	}
	
	@RequestMapping(value = "/persistOrder.html", method = RequestMethod.GET)
	public String persistCreatedOrder(ModelMap modelMap, WebRequest request, Locale locale) 
		throws OrderDuplicateException{
		
		UserAccount userAccount = (UserAccount) modelMap.get("userAccount");
		Order order = (Order) modelMap.get("order");
		
		//persist order and send mails to related persons
		this.placeOrderService.persistNewOrder(userAccount, order, locale);
		
		//refresh rootCategory
		Category rootCategory = (Category) request.getAttribute("rootCategory", WebRequest.SCOPE_SESSION);
		Category sidebarCategory = (Category) request.getAttribute("sidebarCategory", WebRequest.SCOPE_SESSION);
		
		Validate.notNull(rootCategory, "rootCategory shall always not be null");
		Validate.notNull(sidebarCategory, "sidebarCategory shall always not be null");
		
		Long sidebarCategoryId = sidebarCategory.getId();
		
		rootCategory = this.browseBooksService.getRootCategory();
		sidebarCategory = rootCategory.getAllSubCategory(sidebarCategoryId);
		request.setAttribute("rootCategory", rootCategory, WebRequest.SCOPE_SESSION);
		request.setAttribute("sidebarCategory", sidebarCategory, WebRequest.SCOPE_SESSION);
		
		//clean shopping cart
		request.setAttribute("shoppingCart", new ShoppingCart(), WebRequest.SCOPE_SESSION);
		
		modelMap.put("order", order);
		return "redirect:/order/paymentNotice.html";
	}
	
	@RequestMapping(value = "/paymentNotice.html", method = RequestMethod.GET)
	public void goToPaymentNotice(
			@RequestParam(required = false) Long orderId,
			ModelMap modelMap, HttpServletRequest request){
		
		if(null != orderId){
			Order order = this.placeOrderService.getOrderById(orderId);
			order.toString();
			modelMap.put("order", order);
		}
		modelMap.put("previousPath", request.getServletPath());
	}
	@RequestMapping(value = "/orderInfo.html", method = RequestMethod.GET)
	public void seeOrderDetailsInfo(){
		//as a bridge
	}
	

	/**
	 * @return the convenientStoreDeliveryServiceId
	 */
	public Integer getConvenientStoreDeliveryServiceId() {
		return convenientStoreDeliveryServiceId;
	}

	/**
	 * @param convenientStoreDeliveryServiceId the convenientStoreDeliveryServiceId to set
	 */
	public void setConvenientStoreDeliveryServiceId(
			Integer convenientStoreDeliveryServiceId) {
		this.convenientStoreDeliveryServiceId = convenientStoreDeliveryServiceId;
	}

	/**
	 * Validate orderDto by some rules.
	 * @param orderDto
	 * @param result
	 * @param locale
	 */
	private void validateOrderDto(final OrderDto orderDto,
			final BindingResult result, Locale locale) {
		//if payment type is CashInConvenientStore
		if(orderDto.getPaymentType().equals(CashInConvenientStore.class.getSimpleName())){
			//if deliveryService is NOT ConvenientStoreDeliveryService
			if(!orderDto.getDeliveryServiceType().equals(convenientStoreDeliveryServiceId)){
				//error occurs
				result.addError(new FieldError(ORDER_DTO_NAME, "deliveryServiceType", 
						messageSource.getMessage(
								"Pattern.orderDto.deliveryServiceType.notConvenientStoreDeliveryService", 
								null, locale)));
				
			} else{
				//if deliveryService is ConvenientStoreDeliveryService
				
				//if convenientStoreAddress is NOT filled
				if(StringUtils.isBlank(orderDto.getConvenientStoreAddress())){
					//error occurs
					result.addError(new FieldError(ORDER_DTO_NAME, "convenientStoreAddress", 
							messageSource.getMessage(
									"Pattern.orderDto.convenientStoreAddress", 
									null, locale)));
				}
			}
		} else{
			//if payment type is NOT CashInConvenientStore
			
			//if deliveryService is ConvenientStoreDeliveryService
			if(orderDto.getDeliveryServiceType().equals(convenientStoreDeliveryServiceId)){
				//if convenientStoreAddress is NOT filled
				if(StringUtils.isBlank(orderDto.getConvenientStoreAddress())){
					//error occurs
					result.addError(new FieldError(ORDER_DTO_NAME, "convenientStoreAddress", 
							messageSource.getMessage(
									"Pattern.orderDto.convenientStoreAddress", 
									null, locale)));
				}
			} else{
				//if deliveryService is NOT ConvenientStoreDeliveryService
				
				//if recipientAddress is NOT filled
				if(StringUtils.isBlank(orderDto.getRecipientAddress())){
					//error occurs
					result.addError(new FieldError(ORDER_DTO_NAME, "recipientAddress", 
							messageSource.getMessage(
									"Pattern.orderDto.recipientAddress", 
									null, locale)));
				}
			}
		}
		
		//check for invoice type
		//if invoice type is DuplicateInvoice
		if(orderDto.getInvoiceType().equals(DuplicateInvoice.class.getSimpleName())){
			//if invoiceTitle is empty
			if(StringUtils.isBlank(orderDto.getInvoiceTitle())){
				//error occurs
				result.addError(new FieldError(ORDER_DTO_NAME, "invoiceTitle", 
						messageSource.getMessage(
								"Pattern.orderDto.invoiceTitle", 
								null, locale)));
			}
		} else if(orderDto.getInvoiceType().equals(DuplicateInvoiceWithCashier.class.getSimpleName())){
			//if invoice type is DuplicateInvoiceWithCashier
			//if invoiceUniformNumber is empty
			if(StringUtils.isBlank(orderDto.getInvoiceUniformNumber())){
				//error occurs
				result.addError(new FieldError(ORDER_DTO_NAME, "invoiceUniformNumber", 
						messageSource.getMessage(
								"Pattern.orderDto.invoiceUniformNumber", 
								null, locale)));
			}
		} else if(orderDto.getInvoiceType().equals(Invoice.class.getSimpleName())){
			//if invoice type is Invoice
			//if invoiceTitle is empty
			if(StringUtils.isBlank(orderDto.getInvoiceTitle())){
				//error occurs
				result.addError(new FieldError(ORDER_DTO_NAME, "invoiceTitle", 
						messageSource.getMessage(
								"Pattern.orderDto.invoiceTitle", 
								null, locale)));
			}
			//if invoiceUniformNumber is empty
			if(StringUtils.isBlank(orderDto.getInvoiceUniformNumber())){
				//error occurs
				result.addError(new FieldError(ORDER_DTO_NAME, "invoiceUniformNumber", 
						messageSource.getMessage(
								"Pattern.orderDto.invoiceUniformNumber", 
								null, locale)));
			}
			
		}
	}

}
